package com.yn.xgame;

import org.apache.commons.dbutils.QueryRunner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.Arrays;

/**
 * 批处理
 * Created by wangboo on 2016/4/11.
 */
public class BatchExecutor {

    private static final Logger s_logger = LoggerFactory.getLogger(BatchExecutor.class);
    private QueryRunner runner;
    private Connection conn;
    private int batchSize;
    private int argsLength;
    private String headSql;
    private String tailSql;
    private int curIndex;
    private StringBuilder builder = new StringBuilder(1024);
    private Object[] argsList = null;
    private boolean debugEnable;

    private BatchExecutor(QueryRunner runner, Connection conn, int size) {
        this.runner = runner;
        this.conn = conn;
        if (conn != null) try {
            conn.setAutoCommit(true);
        } catch (SQLException e) {
            s_logger.error("", e);
        }
        batchSize = size;
    }

    public static final BatchExecutor of(QueryRunner runner, Connection conn, int batchSize) {
        return new BatchExecutor(runner, conn, batchSize);
    }

    public static final BatchExecutor big(QueryRunner runner, Connection conn) {
        return of(runner, conn, 100);
    }

    public static final BatchExecutor middle(QueryRunner runner, Connection conn) {
        return of(runner, conn, 50);
    }

    public static final BatchExecutor small(QueryRunner runner, Connection conn) {
        return of(runner, conn, 20);
    }

    public BatchExecutor setHeadSql(String sql) {
        this.headSql = sql;
        return this;
    }

    public BatchExecutor setTailSql(String sql) throws Exception {
        this.tailSql = sql;
        return this;
    }

    public BatchExecutor openDebug() {
        debugEnable = true;
        return this;
    }

    public BatchExecutor addValue(Object... args) throws Exception {
        return addValueArray(args);
    }

    public BatchExecutor addValueArray(Object[] args) throws Exception {
        XAssert.isTrue(headSql != null, "headSql must be setted before call addArgs");
        if(argsLength == 0) argsLength = args.length;
        XAssert.isTrue(argsLength == args.length, argsLength, args.length);
        if(builder.length() == 0) {
            argsList = new Object[batchSize * args.length];
            builder.append(headSql);
            if(headSql.endsWith(" ")) {
                builder.append("values");
            }else {
                builder.append(" values");
            }
        }
        System.arraycopy(args, 0, argsList, curIndex * argsLength, args.length);
        // append value
        builder.append("(");
        int argsSize = args.length - 1;
        for (int i=0;i<argsSize;i++) {
            builder.append("?,");
        }
        builder.append("?");
        if(curIndex < batchSize - 1) {
            builder.append("),");
        }else {
            builder.append(") ");
        }
        // execute
        if(curIndex == batchSize - 1) {
            innerEnd();
        }else {
            curIndex += 1;
        }
        return this;
    }

    public void end() throws Exception {
        if(curIndex == 0) return;
        builder.deleteCharAt(builder.length() - 1);
        builder.append(" ");
        if(tailSql != null) builder.append(tailSql);
        Object [] argsList1 = new Object[curIndex * argsLength];
        System.arraycopy(argsList, 0, argsList1, 0, curIndex * argsLength);
        if (debugEnable) {
            s_logger.debug(builder.toString());
            s_logger.debug("args = " + Arrays.toString(argsList1));
        }
        if(conn == null) {
            runner.update(builder.toString(), argsList1);
        }else {
            runner.update(conn, builder.toString(), argsList1);
        }
    }

    private void innerEnd() throws Exception {
        if(tailSql != null) builder.append(tailSql);
        if (debugEnable) {
            s_logger.debug(builder.toString());
            s_logger.debug("args = " + Arrays.toString(argsList));
        }
        if(conn == null) {
            runner.update(builder.toString(), argsList);
        }else {
            runner.update(conn, builder.toString(), argsList);
        }
        builder = new StringBuilder(1024);
        curIndex = 0;
    }


}
